package com.engine.salary.service.auth;

import cn.hutool.core.collection.CollectionUtil;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.annotation.Auth;
import com.engine.salary.entity.auth.dto.*;
import com.engine.salary.entity.auth.param.AuthTreeQueryParam;
import com.engine.salary.entity.auth.vo.Permission;
import com.engine.salary.entity.salarysob.po.SalarySobPO;
import com.engine.salary.enums.auth.AuthCheckTypeEnum;
import com.engine.salary.enums.auth.AuthFilterTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.auth.AuthMapper;
import com.engine.salary.service.SalarySobService;
import com.engine.salary.service.TaxAgentService;
import com.engine.salary.service.impl.SalarySobServiceImpl;
import com.engine.salary.service.impl.TaxAgentServiceImpl;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.google.common.collect.Lists;
import weaver.hrm.User;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class AuthServiceImpl extends Service implements AuthService {

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private AuthRoleService getAuthRoleService(User user) {
        return ServiceUtil.getService(AuthRoleServiceImpl.class, user);
    }

    private AuthMapper getAuthMapper() {
        return MapperProxyFactory.getProxy(AuthMapper.class);
    }

    @Override
    public <T> boolean auth(T t, AuthFilterTypeEnum filterType, Class<T> clazz) {
        List<T> list = new ArrayList<T>();
        list.add(t);
        list = auth(list, filterType, clazz, null);
        return list.size() > 0;
    }

    @Override
    public <T> List<T> auth(List<T> list, AuthFilterTypeEnum filterType, Class<T> clazz) {
        return auth(list, filterType, clazz, null);
    }

    @Override
    public <T> List<T> auth(List<T> list, AuthFilterTypeEnum filterType, Class<T> clazz, String page) {
        Boolean isOpenDevolution = getTaxAgentService(user).isOpenDevolution();
        boolean isAuth = clazz.isAnnotationPresent(Auth.class);

        Auth auth = clazz.getAnnotation(Auth.class);
        String taxAgentIdField = auth.taxAgentIdField();
        String taxAgentIdFieldGetter = "get" + taxAgentIdField.substring(0, 1).toUpperCase() + taxAgentIdField.substring(1);
        String employeeIdField = auth.employeeIdField();
        String employeeIdFieldGetter = "get" + employeeIdField.substring(0, 1).toUpperCase() + employeeIdField.substring(1);
        String sobIdField = auth.sobIdField();
        String sobIdFieldGetter = "get" + sobIdField.substring(0, 1).toUpperCase() + sobIdField.substring(1);
        String optsField = auth.optsField();
        String optsFieldGetter = "get" + optsField.substring(0, 1).toUpperCase() + optsField.substring(1);
        String optsFieldSetter = "set" + optsField.substring(0, 1).toUpperCase() + optsField.substring(1);

        //不开启分权
        if (!isOpenDevolution) {
            if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                list.forEach(t -> {
                    try {
                        Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                        Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                        if (opts == null) {
                            opts = new HashSet<>();
                        }
                        opts.add("query");
                        opts.add("admin");

                        Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                        optsFieldSetterMethod.invoke(t, opts);
                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        throw new SalaryRunTimeException(e);
                    }
                });
            }
            return list;
        }

        //未配置分权
        if (!isAuth) {
            return list;
        }

        //给总管理员赋值最大权限
        Boolean isChief = getTaxAgentService(user).isChief((long) user.getUID());
        if (isChief || filterType == AuthFilterTypeEnum.NO_AUTH) {
            if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                list.forEach(t -> {
                    try {
                        Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                        Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                        if (opts == null) {
                            opts = new HashSet<>();
                        }
                        opts.add("query");
                        opts.add("admin");

                        Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                        optsFieldSetterMethod.invoke(t, opts);
                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        throw new SalaryRunTimeException(e);
                    }
                });
            }
            return list;
        }

        List<T> resultList = new ArrayList<>();

        page = page == null ? auth.page() : page;
        AuthCheckTypeEnum checkType = auth.checkType();

        //给各管理员赋值对应的扣缴义务人权限
        List<Long> adminTaxAgentIds = getTaxAgentService(user).getAdminTaxAgentIds((long) user.getUID());
        if (CollectionUtil.isNotEmpty(adminTaxAgentIds)) {
            Iterator<T> iterator = list.iterator();
            if (checkType == AuthCheckTypeEnum.TAX || checkType == AuthCheckTypeEnum.TAX_EMP) {
                while (iterator.hasNext()) {
                    try {
                        T t = iterator.next();
                        Method taxAgentIdFieldGetterMethod = t.getClass().getMethod(taxAgentIdFieldGetter);
                        Long taxAgentId = (Long) taxAgentIdFieldGetterMethod.invoke(t);

                        if (adminTaxAgentIds.contains(taxAgentId)) {
                            if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                                Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                                Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                                if (opts == null) {
                                    opts = new HashSet<>();
                                }
                                opts.add("query");
                                opts.add("admin");

                                Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                                optsFieldSetterMethod.invoke(t, opts);
                            }
                            resultList.add(t);
                        }
                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        throw new SalaryRunTimeException(e);
                    }
                }
            } else if (checkType == AuthCheckTypeEnum.SOB) {
                List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByTaxAgentIds(adminTaxAgentIds);
                Set<Long> adminSobIds = SalaryEntityUtil.properties(salarySobPOS, SalarySobPO::getId);
                while (iterator.hasNext()) {
                    try {
                        T t = iterator.next();
                        Method sobIdFieldGetterMethod = t.getClass().getMethod(sobIdFieldGetter);
                        Long sobId = (Long) sobIdFieldGetterMethod.invoke(t);
                        if (adminSobIds.contains(sobId)) {
                            if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                                Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                                Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                                if (opts == null) {
                                    opts = new HashSet<>();
                                }
                                opts.add("query");
                                opts.add("admin");

                                Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                                optsFieldSetterMethod.invoke(t, opts);
                            }
                            resultList.add(t);
                        }
                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        throw new SalaryRunTimeException(e);
                    }
                }
            }
        }

        list.removeAll(resultList);

        //给各角色赋权
        try {
            Iterator<T> iterator = list.iterator();
            if (checkType == AuthCheckTypeEnum.TAX_EMP) {

                List<TaxEmpOptAuth> authDTOS = getAuthMapper().getTaxEmpOptAuth((long) user.getUID(), page);
                Map<Long, List<EmpOpt>> authMap = SalaryEntityUtil.convert2Map(authDTOS, TaxEmpOptAuth::getTaxAgentId, TaxEmpOptAuth::getEmps);

                while (iterator.hasNext()) {
                    T t = iterator.next();
                    //混合验证
                    Method taxAgentIdFieldGetterMethod = t.getClass().getMethod(taxAgentIdFieldGetter);
                    Long taxAgentId = (Long) taxAgentIdFieldGetterMethod.invoke(t);
                    Method employeeIdFieldGetterMethod = t.getClass().getMethod(employeeIdFieldGetter);
                    Long employeeId = (Long) employeeIdFieldGetterMethod.invoke(t);

                    if (authMap.containsKey(taxAgentId)) {
                        List<EmpOpt> orDefault = authMap.getOrDefault(taxAgentId, new ArrayList<>());
                        Map<Long, Set<String>> optsMap = SalaryEntityUtil.convert2Map(orDefault, EmpOpt::getEmployeeId, EmpOpt::getOpts);
                        if (optsMap.containsKey(employeeId)) {
                            Set<String> optSets = optsMap.getOrDefault(employeeId, new HashSet<>());
                            if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                                Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                                Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                                if (opts == null) {
                                    opts = new HashSet<>();
                                }
                                opts.addAll(optSets);

                                Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                                optsFieldSetterMethod.invoke(t, opts);
                                resultList.add(t);
                            }
                            if (filterType == AuthFilterTypeEnum.QUERY_DATA) {
                                resultList.add(t);
                            }
                            if (filterType == AuthFilterTypeEnum.ADMIN_DATA) {
                                if (optSets.contains("admin")) {
                                    resultList.add(t);
                                }
                            }
                        }
                    }
                }
            } else if (checkType == AuthCheckTypeEnum.SOB) {
                List<SobOptAuth> sobOptAuth = getAuthMapper().getSobOptAuth((long) user.getUID(), page);
                Map<Long, Set<String>> sobOpts = SalaryEntityUtil.convert2Map(sobOptAuth, SobOptAuth::getSobId, SobOptAuth::getOpts);
                while (iterator.hasNext()) {
                    T t = iterator.next();
                    Method sobIdFieldGetterMethod = t.getClass().getMethod(sobIdFieldGetter);
                    Long sobId = (Long) sobIdFieldGetterMethod.invoke(t);

                    if (sobOpts.containsKey(sobId)) {
                        Set<String> optSets = sobOpts.getOrDefault(sobId, new HashSet<>());
                        if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                            Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                            Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                            if (opts == null) {
                                opts = new HashSet<>();
                            }
                            opts.addAll(optSets);

                            Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                            optsFieldSetterMethod.invoke(t, opts);
                            resultList.add(t);
                        }
                        if (filterType == AuthFilterTypeEnum.QUERY_DATA) {
                            resultList.add(t);
                        }
                        if (filterType == AuthFilterTypeEnum.ADMIN_DATA) {
                            if (optSets.contains("admin")) {
                                resultList.add(t);
                            }
                        }
                    }
                }
            } else if (checkType == AuthCheckTypeEnum.TAX) {
                List<TaxOptAuth> authDTOS = getAuthMapper().getTaxOptAuth((long) user.getUID(), page);
                Map<Long, Set<String>> taxOpts = SalaryEntityUtil.convert2Map(authDTOS, TaxOptAuth::getTaxAgentId, TaxOptAuth::getOpts);

                while (iterator.hasNext()) {
                    T t = iterator.next();
                    Method taxAgentIdFieldGetterMethod = t.getClass().getMethod(taxAgentIdFieldGetter);
                    Long taxAgentId = (Long) taxAgentIdFieldGetterMethod.invoke(t);

                    if (taxOpts.containsKey(taxAgentId)) {
                        Set<String> optSets = taxOpts.getOrDefault(taxAgentId, new HashSet<>());
                        if (filterType == AuthFilterTypeEnum.DATA_OPT) {
                            Method optsFieldGetterMethod = t.getClass().getMethod(optsFieldGetter);
                            Set<String> opts = (Set<String>) optsFieldGetterMethod.invoke(t);
                            if (opts == null) {
                                opts = new HashSet<>();
                            }
                            opts.addAll(optSets);

                            Method optsFieldSetterMethod = t.getClass().getMethod(optsFieldSetter, Set.class);
                            optsFieldSetterMethod.invoke(t, opts);
                            resultList.add(t);
                        }
                        if (filterType == AuthFilterTypeEnum.QUERY_DATA) {
                            resultList.add(t);
                        }
                        if (filterType == AuthFilterTypeEnum.ADMIN_DATA) {
                            if (optSets.contains("admin")) {
                                resultList.add(t);
                            }
                        }
                    }
                }
            }
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            throw new SalaryRunTimeException(e);
        }
        return resultList;
    }

    @Override
    public Permission permission(String page) {
        long uid = user.getUID();
        List<String> opts = new ArrayList<>();

        //获取是否开启分权模式
        Boolean isOpenDevolution = getTaxAgentService(user).isOpenDevolution();
        //给总管理员赋值最大权限
        Boolean isChief = getTaxAgentService(user).isChief(uid);
        //给各管理员赋值对应的扣缴义务人权限
        Boolean isAdminEnable = getTaxAgentService(user).isAdminEnable(uid);

        boolean able = false;

        //不需要鉴权的页面
        List<String> noAuthPage = Lists.newArrayList("myBill");

        //只允许总管理进去的界面
        List<String> chiefPage = Lists.newArrayList("auth");

        if (noAuthPage.contains(page)) {
            opts.add("admin");
            able = true;
        } else if (chiefPage.contains(page)) {
            opts.add("admin");
            able = isChief;
        } else {
            //如果是管理员，赋值管理权限返回
            if (isChief || isAdminEnable) {
                opts.add("admin");
                able = true;
            } else {
                opts = getAuthMapper().getOptsByPage(uid, page);
                able = CollectionUtil.isNotEmpty(opts);
            }
        }

        return Permission.builder()
                .isOpenDevolution(isOpenDevolution)
                .isChief(isChief)
                .isAdminEnable(isAdminEnable)
                .able(able)
                .opts(opts)
                .build();
    }

    @Override
    public AuthTreeDTO tree(AuthTreeQueryParam param) {
        AuthTreeDTO authTreeDTO = getAuthMapper().authTree(param);
        return authTreeDTO;
    }

}
